use std::thread;
use std::time::Duration;

#[cfg(test)]
mod closure_fn {
    fn exec<F: FnOnce()>(f: F) {
        f()
    }

    fn exec1<F: FnMut()>(mut f: F) {
        f()
    }

    fn exec2<F: Fn()>(f: F) {
        f()
    }

    #[test]
    fn main() {
        let s = String::new();

        let update_string = || println!("{}", s);

        exec(update_string);
        exec1(update_string);
        exec2(update_string);
    }
}

struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}

fn generate_workout(intensity: u32, random_number: u32) {
    let mut expensive_result = Cacher::new(|num| {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    });

    //只执行第一次 而慢计算最多只会运行一次。
    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_result.value(intensity));
        println!("Next, do {} situps!", expensive_result.value(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!(
                "Today, run for {} minutes!",
                expensive_result.value(intensity)
            );
        }
    }
}

#[cfg(test)]
mod closure_example {
    #[test]
    fn func_closure() {}
}

#[cfg(test)]
mod closure_test {
    use std::thread;
    use std::time::Duration;

    fn workout(intensity: u32, random_number: u32) {
        let action = || {
            println!("muuuu.....");
            thread::sleep(Duration::from_millis(500));
            intensity
        };

        if intensity < 25 {
            println!("今天活力满满，先做 {} 个俯卧撑!", action());
            println!("旁边有妹子在看，俯卧撑太low，再来 {} 组卧推!", action());
        } else if random_number == 3 {
            println!("昨天练过度了，今天还是休息下吧！");
        } else {
            println!("昨天练过度了，今天干干有氧，跑步 {} 分钟!", action());
        }
    }

    #[test]
    fn main() {
        // 动作次数
        let intensity = 10;
        // 随机值用来决定某个选择
        let random_number = 7;

        // 开始健身
        workout(intensity, random_number);
    }
}

// 闭包捕获变量有三种途径，恰好对应函数参数的三种传入方式：
// 转移所有权、可变借用、不可变借用

// FnOnce < FnMut < FN 权限
// 当创建一个闭包时，Rust 根据其如何使用环境中变量来推断我们希望如何引用环境
// 由于所有闭包都可以被调用至少一次，所以所有闭包都实现了 FnOnce
// 那些并没有移动被捕获变量的所有权到闭包内的闭包也实现了 FnMut
// 而不需要对被捕获的变量进行可变访问的闭包则也实现了 Fn
#[cfg(test)]
mod closure_params {
    #[test]
    fn func_closure() {
        let add = |x| x + 1;
        let x = 5;
        println!("x {} y {}", x, add(x));

        // 捕获作用域中的值: x
        let eq = |y| x == y + 1;
        println!("{}", eq(x - 1));
    }

    // FnOnce，该类型的闭包会拿走被捕获变量的所有权。
    // 为了消费捕获到的变量，闭包必须获取其所有权并在定义闭包时将其移动进闭包。
    // Once 顾名思义，说明该闭包只能运行一次：闭包不能多次获取相同变量的所有权的事实
    fn fn_once<F>(func: F)
    where
        F: FnOnce(usize) -> bool,
    {
        println!("{}", func(3)); //仅实现 FnOnce 特征的闭包在调用时会转移所有权
        // println!("{}", func(4)); // err  which causes it to be moved when called
    }

    // 实现copy 解决所有权转义
    fn fn_once_copy<F>(func: F)
    where
        F: FnOnce(usize) -> bool + Copy, // 改动在这里
    {
        println!("{}", func(3));
        println!("{}", func(4));
    }

    #[test]
    fn func_once_main() {
        let x = vec![1, 2, 3];
        fn_once(|z| z == x.len());
        fn_once_copy(|z| z == x.len());
    }

    #[test]
    fn func_once_move() {
        use std::thread;
        let v = vec![1, 2, 3];
        // 强制闭包取得捕获变量的所有权，可以在参数列表前添加 move 关键字
        // 这种用法通常用于闭包的生命周期大于捕获变量的生命周期时，例如将闭包返回或移入其他线程。
        let handle = thread::spawn(move || {
            println!("Here's a vector: {:?}", v);
        });
        handle.join().unwrap();
    }

    // FnMut，它以可变借用的方式捕获了环境中的值，获取可变的借用值所以可以改变其环境
    #[test]
    fn func_mut() {
        let mut s = String::new();
        //想要在闭包内部捕获可变借用，需要把该闭包声明为可变类型
        // let update_string = |str| s.push_str(str);
        let mut update_string = |str| s.push_str(str);
        update_string("hello");
        println!("{:?}", s);
    }

    fn exec_mut<'a, F: FnMut(&'a str)>(mut f: F) {
        f("hello")
    }

    // FnMut是推导出的特征类型，mut是rust语言层面的一个修饰符，用于声明一个绑定是可变的
    #[test]
    fn func_mut_new() {
        let mut s = String::from("aaaa");
        // let mut update_string = |str| s.push_str(str);

        // update_string没有使用mut关键字修饰，而上文提到想要在闭包内部捕获可变借用，需要用关键词把该闭包声明为可变类型

        // let update_string: impl FnMut(&str) =  |str| s.push_str(str);
        let update_string = |str| s.push_str(str);
        exec_mut(update_string);
        println!("{:?}", s);
    }

    // 闭包自动实现Copy特征的规则是，只要闭包捕获的类型都实现了Copy特征->这个闭包就会默认实现Copy特征

    #[test]
    fn func_mut_copy() {
        // 拿所有权
        let s = String::new();
        let update_string = move || println!("{}", s);

        // exec_mut(update_string);
        // exec2(update_string); // 不能再用了

        // 可变引用
        //         let mut ms = String::new();
        //         let mut update_string = || ms.push_str("hello");
        //         exec_mut(update_string);
        // exec1(update_string); // 不能再用了
    }

    #[test]
    fn func_fn() {
        // 拿所有权
        let s = String::new();
        let update_string = move || println!("{}", s); // 拿到了s的所有权
        update_string();
        // println!("{}", s); // 此处调用失败
    }
}
